"use strict";

const fs = require("fs");
const path = require("path");
const webpack = require("webpack");
const resolve = require("resolve");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CaseSensitivePathsPlugin = require("case-sensitive-paths-webpack-plugin");
const InlineChunkHtmlPlugin = require("react-dev-utils/InlineChunkHtmlPlugin");
const TerserPlugin = require("terser-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const getCSSModuleLocalIdent = require("react-dev-utils/getCSSModuleLocalIdent");
const paths = require("./paths");
const ModuleNotFoundPlugin = require("react-dev-utils/ModuleNotFoundPlugin");
const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");
// const ForkTsCheckerWebpackPlugin = process.env.TSC_COMPILE_ON_ERROR === "true" ? require("react-dev-utils/ForkTsCheckerWarningWebpackPlugin") : require("react-dev-utils/ForkTsCheckerWebpackPlugin");

// Source maps are resource heavy and can cause out of memory issue for large source files.
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== "false";

const babelRuntimeEntry = require.resolve("babel-preset-react-app");

// Some apps do not need the benefits of saving a web request, so not inlining the chunk
// makes for a smoother build process.
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== "false";

const imageInlineSizeLimit = parseInt(process.env.IMAGE_INLINE_SIZE_LIMIT || "10000");

// Check if TypeScript is setup
const useTypeScript = fs.existsSync(paths.appTsConfig);

// style files regexes
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;

const hasJsxRuntime = (() => {
	if (process.env.DISABLE_NEW_JSX_TRANSFORM === "true") {
		return false;
	}

	try {
		require.resolve("react/jsx-runtime");
		return true;
	} catch (e) {
		return false;
	}
})();
const isEnvDevelopment = process.env.NODE_ENV === "development";
const isEnvProduction = process.env.NODE_ENV === "production";

module.exports = function getBaseWebpackConfig(isServer) {
	// common function to get style loaders
	const getStyleLoaders = (cssOptions, preProcessor) => {
		const loaders = [
			isServer || isEnvProduction ? MiniCssExtractPlugin.loader : require.resolve("style-loader"),
			{
				loader: require.resolve("css-loader"),
				options: cssOptions,
			},
			{
				loader: require.resolve("postcss-loader"),
				options: {
					postcssOptions: {
						ident: "postcss",
						config: false,
						plugins: [
							"postcss-flexbugs-fixes",
							[
								"postcss-preset-env",
								{
									autoprefixer: {
										flexbox: "no-2009",
									},
									stage: 3,
								},
							],
							"postcss-normalize",
						],
					},
					sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
				},
			},
		].filter(Boolean);
		if (preProcessor) {
			loaders.push(
				{
					loader: require.resolve("resolve-url-loader"),
					options: {
						sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
						root: paths.appSrc,
					},
				},
				{
					loader: require.resolve(preProcessor),
					options: {
						sourceMap: true,
					},
				}
			);
		}
		return loaders;
	};

	return {
		mode: isEnvProduction ? "production" : isEnvDevelopment && "development",
		devtool: "source-map",
		output: {
			path: paths.appBuild,
			publicPath: "/",
			chunkFilename: isEnvProduction ? "public/js/[name].[contenthash:8].chunk.js" : isEnvDevelopment && "public/js/[name].chunk.js",
			// pathinfo: isEnvDevelopment,
			// filename: isEnvProduction ? "static/js/[name].[contenthash:8].js" : isEnvDevelopment && "static/js/bundle.js",
			// assetModuleFilename: "static/media/[name].[hash][ext]",
		},
		optimization: {
			minimize: isEnvProduction,
			splitChunks: {
				cacheGroups: {
					subVendor: {
						chunks: 'async',
						name: 'subVendor',
						minChunks: 5,
					},
				},
			},
			minimizer: [
				// This is only used in production mode
				new TerserPlugin({
					terserOptions: {
						parse: {
							ecma: 8,
						},
						compress: {
							ecma: 5,
							warnings: false,
							// Disabled because of an issue with Uglify breaking seemingly valid code:
							// https://github.com/facebook/create-react-app/issues/2376
							// Pending further investigation:
							// https://github.com/mishoo/UglifyJS2/issues/2011
							comparisons: false,
							// Disabled because of an issue with Terser breaking valid code:
							// https://github.com/facebook/create-react-app/issues/5250
							// Pending further investigation:
							// https://github.com/terser-js/terser/issues/120
							inline: 2,
						},
						mangle: {
							safari10: true,
						},
						// Added for profiling in devtools
						output: {
							ecma: 5,
							comments: false,
							// Turned on because emoji and regex is not minified properly using default
							// https://github.com/facebook/create-react-app/issues/2488
							ascii_only: true,
						},
					},
				}),
				// This is only used in production mode
				new CssMinimizerPlugin(),
			],
		},
		resolve: {
			// 寻找 node_modules 的路径
			modules: [path.resolve("node_modules"), "node_modules"],
			extensions: paths.moduleFileExtensions.map(ext => `.${ext}`).filter(ext => useTypeScript || !ext.includes("ts")),
			alias: {},
			symlinks: false,
		},
		module: {
			strictExportPresence: true,
			rules: [
				{
					oneOf: [
						{
							test: [/\.avif$/],
							type: "asset",
							mimetype: "image/avif",
							parser: {
								dataUrlCondition: {
									maxSize: imageInlineSizeLimit,
								},
							},
						},
						{
							test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
							type: "asset",
							parser: {
								dataUrlCondition: {
									maxSize: imageInlineSizeLimit,
								},
							},
						},
						{
							test: /\.svg$/,
							use: [
								{
									loader: require.resolve("@svgr/webpack"),
									options: {
										prettier: false,
										svgo: false,
										svgoConfig: {
											plugins: [{ removeViewBox: false }],
										},
										titleProp: true,
										ref: true,
									},
								},
								{
									loader: require.resolve("file-loader"),
									options: {
										name: "static/media/[name].[hash].[ext]",
									},
								},
							],
							issuer: {
								and: [/\.(ts|tsx|js|jsx|md|mdx)$/],
							},
						},
						// Process application JS with Babel.
						// The preset includes JSX, Flow, TypeScript, and some ESnext features.
						{
							test: /\.(js|mjs|jsx|ts|tsx)$/,
							include: [paths.appSrc, paths.ssrPath].filter(Boolean),
							loader: require.resolve("babel-loader"),
							options: {
								customize: require.resolve("babel-preset-react-app/webpack-overrides"),
								sourceType: "unambiguous",
								presets: [
									['@babel/preset-env', Object.assign({loose: true, bugfixes: true, modules: false, debug: isEnvProduction, useBuiltIns: 'usage', corejs: '3.22'}, isServer && {targets: {node: '12.0.0'}})],
									"react-app",
									[
										"@babel/preset-typescript",
										{
											allExtensions: true,
											isTSX: true,
											allowDeclareFields: true,
										},
									],
								].filter(Boolean),
								plugins: [isServer && "dynamic-import-node","@loadable/babel-plugin"].filter(Boolean),
								cacheDirectory: true,
								cacheCompression: isEnvProduction,
								compact: isEnvProduction,
							},
						},
						// // // 某些npn包不是ES5实现的，也需要babel编译
						// {
						// 	test: /\.(js|mjs|jsx|ts|tsx)$/,
						// 	loader: require.resolve("babel-loader"),
						// 	include: [/@loadable/],
						// 	options: {
						// 		presets: [
						// 			[
						// 				"@babel/preset-env",
						// 				{
						// 					modules: 'commonjs',
						// 					debug: isEnvProduction,
						// 					useBuiltIns: "usage",
						// 					corejs: 3,
						// 				},
						// 			],
						// 		],
						// 		cacheDirectory: true,
						// 		cacheCompression: isEnvProduction,
						// 		compact: isEnvProduction,
						// 	},
						// },
						// Process any JS outside of the app with Babel.
						// Unlike the application JS, we only compile the standard ES features.
						{
							test: /\.(js|mjs)$/,
							exclude: /@babel(?:\/|\\{1,2})runtime/,
							loader: require.resolve('babel-loader'),
							options: {
								babelrc: false,
								configFile: false,
								compact: isEnvProduction,
								presets: [[require.resolve('babel-preset-react-app/dependencies'), {helpers: true}]],
								cacheDirectory: true,
								cacheCompression: isEnvProduction,
								sourceMaps: false,
							},
						},
						{
							test: cssRegex,
							exclude: cssModuleRegex,
							use: getStyleLoaders({
								importLoaders: 1,
								sourceMap: shouldUseSourceMap,
							}),
							sideEffects: true,
						},
						{
							test: cssModuleRegex,
							use: getStyleLoaders({
								importLoaders: 1,
								sourceMap: shouldUseSourceMap,
								modules: true,
								getLocalIdent: getCSSModuleLocalIdent,
							}),
						},
						// Opt-in support for SASS (using .scss or .sass extensions).
						// By default we support SASS Modules with the
						// extensions .module.scss or .module.sass
						{
							test: sassRegex,
							exclude: sassModuleRegex,
							use: getStyleLoaders(
								{
									importLoaders: 3,
									sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
									modules: {
										mode: "icss",
									},
								},
								"sass-loader"
							),
							// Don't consider CSS imports dead code even if the
							// containing package claims to have no side effects.
							// Remove this when webpack adds a warning or an error for this.
							// See https://github.com/webpack/webpack/issues/6571
							sideEffects: true,
						},
						// Adds support for CSS Modules, but using SASS
						// using the extension .module.scss or .module.sass
						{
							test: sassModuleRegex,
							use: getStyleLoaders(
								{
									importLoaders: 3,
									sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
									modules: {
										mode: "local",
										getLocalIdent: getCSSModuleLocalIdent,
									},
								},
								"sass-loader"
							),
						},
						// "file" loader makes sure those assets get served by WebpackDevServer.
						// When you `import` an asset, you get its (virtual) filename.
						// In production, they would get copied to the `build` folder.
						// This loader doesn't use a "test" so it will catch all modules
						// that fall through the other loaders.
						{
							// Exclude `js` files to keep "css" loader working as it injects
							// its runtime that would otherwise be processed through "file" loader.
							// Also exclude `html` and `json` extensions so they get processed
							// by webpacks internal loaders.
							exclude: [/^$/, /\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
							type: "asset/resource",
						},
						// ** STOP ** Are you adding a new loader?
						// Make sure to add the new loader(s) before the "file" loader.
					],
				},
			].filter(Boolean),
		},
		plugins: [
			// Inlines the webpack runtime script. This script is too small to warrant
			// a network request.
			// https://github.com/facebook/create-react-app/issues/5358
			isEnvProduction && shouldInlineRuntimeChunk && new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]),
			// This gives some necessary context to module not found errors, such as
			// the requesting resource.
			new ModuleNotFoundPlugin(paths.appPath),
			// Makes some environment variables available to the JS code, for example:
			// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
			// It is absolutely essential that NODE_ENV is set to production
			// during a production build.
			// Otherwise React will be compiled in the very slow development mode.
			new webpack.DefinePlugin({
				"process.env.NODE_ENV": JSON.stringify(isEnvProduction ? "production" : "development"),
			}),
			isEnvDevelopment && new CaseSensitivePathsPlugin(),
			(isEnvProduction || isServer) &&
				new MiniCssExtractPlugin({
					filename: "public/css/[name].[contenthash].css",
					chunkFilename: "public/css/[name].[contenthash].chunk.css",
				}),
			new webpack.IgnorePlugin({
				resourceRegExp: /^\.\/locale$/,
				contextRegExp: /moment$/,
			}),
			// TypeScript type checking
			useTypeScript &&
				new ForkTsCheckerWebpackPlugin({
					async: isEnvDevelopment,
					typescript: {
						typescriptPath: resolve.sync("typescript", {
							basedir: paths.appNodeModules,
						}),
						context: paths.appPath,
						diagnosticOptions: {
							syntactic: true,
							semantic: true,
							declaration: true,
							global: true,
						},
						mode: "write-references",
						// profile: true,
					},
					devServer: false,
				}),
		].filter(Boolean),
		// Turn off performance processing because we utilize
		// our own hints via the FileSizeReporter
		performance: false,
	};
};
